package sk.stuba.fiit.repka.dp.owl.gui;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToolBar;
import javax.swing.JTree;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import org.apache.log4j.Logger;

import sk.stuba.fiit.repka.dp.gui.MyButton;
import sk.stuba.fiit.repka.dp.owl.MyOWLClass;
import sk.stuba.fiit.repka.dp.owl.MyOWLDataProperty;
import sk.stuba.fiit.repka.dp.owl.MyOWLObjectProperty;
import sk.stuba.fiit.repka.dp.owl.OWLComponent;
import sk.stuba.fiit.repka.dp.owl.OWLProperty;

public class OWLPropertiesPanel extends JPanel{
	private static Logger logger = Logger.getLogger(OWLPropertiesPanel.class);
	
	private Map<MyDeleteBT, MyOWLClass> domainMapper = new HashMap<MyDeleteBT, MyOWLClass>(); //map domains to buttons
	private Map<MyDeleteBT, String> rangeMapper = new HashMap<MyDeleteBT, String>(); //map ranges to buttons
	
	private JTree tree;
	private DefaultMutableTreeNode rootNode;
	private DefaultTreeModel treeModel;
	private MyButton btnAdd;
	private MyButton btnRemove;
	private MyButton btnDomain;
	private MyButton btnRange;
	private MyButton btnRecommendations;
	private JPanel rangePanel; 
	private JPanel domainPanel;
	
	private MainPanel parent;
	
	protected JTree getTree() {
		return tree;
	}
	
	protected DefaultMutableTreeNode getRootNode() {
		return rootNode;
	}

	protected DefaultTreeModel getTreeModel() {
		return treeModel;
	}

	protected MyButton getBtnAdd() {
		return btnAdd;
	}

	protected MyButton getBtnRemove() {
		return btnRemove;
	}

	protected MyButton getBtnDomain() {
		return btnDomain;
	}

	protected MyButton getBtnRange() {
		return btnRange;
	}

	protected MyButton getBtnRecommendations() {
		return btnRecommendations;
	}

	protected void setToolbarButtons(JToolBar toolBar) {
		GridBagLayout gridBagLayout = new GridBagLayout();
		gridBagLayout.columnWidths = new int[]{0, 0};
		gridBagLayout.rowHeights = new int[]{0, 0, 0, 0, 0, 0};
		gridBagLayout.columnWeights = new double[]{0.0, Double.MIN_VALUE};
		gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE};
		toolBar.setLayout(gridBagLayout);
		GridBagConstraints gbc = new GridBagConstraints();
		gbc.insets = new Insets(10, 0, 0, 0);
		
		btnAdd = new MyButton("Add");
		gbc.gridx = 0;
		gbc.gridy = 0;
		toolBar.add(btnAdd, gbc);
		
		btnRemove = new MyButton("Remove");
		gbc.gridx = 0;
		gbc.gridy = 1;
		toolBar.add(btnRemove, gbc);
		
		btnDomain = new MyButton("Domain");
		gbc.gridx = 0;
		gbc.gridy = 2;
		toolBar.add(btnDomain, gbc);
		
		btnRange = new MyButton("Range");
		gbc.gridx = 0;
		gbc.gridy = 3;
		toolBar.add(btnRange, gbc);
		
		btnRecommendations = new MyButton("Help");
		gbc.gridx = 0;
		gbc.gridy = 4;
		toolBar.add(btnRecommendations, gbc);
	}
	
	/**
	 * Create the panel.
	 */
	public OWLPropertiesPanel(MainPanel parent) {
		this.parent = parent;
		GridBagLayout gridBagLayout = new GridBagLayout();
		gridBagLayout.columnWidths = new int[]{110, 332, 356, 0};
		gridBagLayout.rowHeights = new int[]{0, 0, 0};
		gridBagLayout.columnWeights = new double[]{0.0, 1.0, 1.0, Double.MIN_VALUE};
		gridBagLayout.rowWeights = new double[]{1.0, 1.0, Double.MIN_VALUE};
		setLayout(gridBagLayout);
		
		JToolBar toolBar = new JToolBar();
		toolBar.setOrientation(SwingConstants.VERTICAL);
		GridBagConstraints gbc_toolBar = new GridBagConstraints();
		gbc_toolBar.insets = new Insets(0, 0, 0, 5);
		gbc_toolBar.gridheight = 2;
		gbc_toolBar.fill = GridBagConstraints.BOTH;
		gbc_toolBar.gridx = 0;
		gbc_toolBar.gridy = 0;
		add(toolBar, gbc_toolBar);
		
		this.setToolbarButtons(toolBar);
		
		JScrollPane propertiesScrollPane = new JScrollPane();
		propertiesScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		propertiesScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		GridBagConstraints gbc_propertiesScrollPane = new GridBagConstraints();
		gbc_propertiesScrollPane.insets = new Insets(0, 0, 0, 5);
		gbc_propertiesScrollPane.gridheight = 2;
		gbc_propertiesScrollPane.fill = GridBagConstraints.BOTH;
		gbc_propertiesScrollPane.gridx = 1;
		gbc_propertiesScrollPane.gridy = 0;
		add(propertiesScrollPane, gbc_propertiesScrollPane);
		
		this.rootNode = new DefaultMutableTreeNode("Top Property");
		this.treeModel = new DefaultTreeModel(this.rootNode);
		this.tree = new JTree(treeModel);
		tree.setEditable(false);
		tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
		tree.setShowsRootHandles(true);
		propertiesScrollPane.setViewportView(tree);
		
		JScrollPane domainScrollPane = new JScrollPane();
		domainScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		domainScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		domainScrollPane.setBorder(BorderFactory.createTitledBorder("Domain"));
		domainPanel = new JPanel();
		domainPanel.setLayout(new GridBagLayout());
		domainScrollPane.setViewportView(domainPanel);
		GridBagConstraints gbc_domainScrollPane = new GridBagConstraints();
		gbc_domainScrollPane.insets = new Insets(0, 0, 5, 0);
		gbc_domainScrollPane.fill = GridBagConstraints.BOTH;
		gbc_domainScrollPane.gridx = 2;
		gbc_domainScrollPane.gridy = 0;
		add(domainScrollPane, gbc_domainScrollPane);
		
		JScrollPane rangeScrollPane = new JScrollPane();
		rangeScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		rangeScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		rangeScrollPane.setBorder(BorderFactory.createTitledBorder("Range"));
		rangePanel = new JPanel();
		rangePanel.setLayout(new GridBagLayout());
		rangeScrollPane.setViewportView(rangePanel);
		GridBagConstraints gbc_rangeScrollPane = new GridBagConstraints();
		gbc_rangeScrollPane.fill = GridBagConstraints.BOTH;
		gbc_rangeScrollPane.gridx = 2;
		gbc_rangeScrollPane.gridy = 1;
		add(rangeScrollPane, gbc_rangeScrollPane);
		
	}
	
	protected void createTreeNodes(DefaultMutableTreeNode top, List<? extends OWLComponent> classes) {
		DefaultMutableTreeNode clazz = null;
		for(OWLComponent cl : classes) {
			clazz = new DefaultMutableTreeNode(cl.getName());
			top.add(clazz);
			if(cl.hasSubComponent()) {
				this.createTreeNodes(clazz, cl.getSubComponents());
			}
		}
	}
	
	protected String addNewNode(String child) {
		DefaultMutableTreeNode parentNode = null;
		TreePath parentPath = this.getTree().getSelectionPath();

		if (parentPath == null) {
			parentNode = this.getRootNode();
		} else {
			parentNode = (DefaultMutableTreeNode) (parentPath.getLastPathComponent());
		}
		DefaultMutableTreeNode childNode = new DefaultMutableTreeNode(child);
		this.getTreeModel().insertNodeInto(childNode, parentNode, parentNode.getChildCount());
		return parentNode.getUserObject().toString();
	}
	
	protected String removeCurrentNode() {
		Toolkit toolkit = Toolkit.getDefaultToolkit();
		TreePath currentSelection = this.getTree().getSelectionPath();
		if (currentSelection != null) {
			DefaultMutableTreeNode currentNode = (DefaultMutableTreeNode) (currentSelection.getLastPathComponent());
			MutableTreeNode parent = (MutableTreeNode) (currentNode.getParent());
			if (parent != null) {
				this.getTreeModel().removeNodeFromParent(currentNode);
				return currentNode.getUserObject().toString();
			}
		}

		// Either there was no selection, or the root was selected.
		toolkit.beep();
		return "";
	}
	
	protected void removeComponentFromList(String nodeToRemove, List<? extends OWLComponent> classes) {
		Iterator<? extends OWLComponent> iter = classes.iterator();
		while(iter.hasNext()) {
			OWLComponent o = iter.next();
			if(o.getName().equals(nodeToRemove)) {
				iter.remove();
				return;
			}
			if(o.hasSubComponent()) {
				this.removeComponentFromList(nodeToRemove, o.getSubComponents());
			}
		}
	}
	
	protected OWLComponent findOWLComponentByName(String componentName, List<? extends OWLComponent> propertyList) {
		OWLComponent result = null;
		Iterator<? extends OWLComponent> iter = propertyList.iterator();
		while(iter.hasNext()) {
			OWLComponent o = iter.next();
			if(componentName.equals(o.getName())) {
				result = o;
			} else if(o.hasSubComponent()) {
				result = this.findOWLComponentByName(componentName, o.getSubComponents());
			}
		}
		return result;
	}
	
	protected void fillDomainPanel(OWLProperty property) {
		this.domainMapper = new HashMap<MyDeleteBT, MyOWLClass>(); //clear hashmap
		this.domainPanel.removeAll(); //clear panel 
		List<MyOWLClass> domainList = property.getDomains();
		Iterator<MyOWLClass> iter = domainList.iterator();
		int y = 0;
		while(iter.hasNext()) {
			MyOWLClass o = iter.next();
			
			GridBagConstraints gbc = new GridBagConstraints();
			gbc.insets = new Insets(0, 0, 5, 20);
			gbc.anchor = GridBagConstraints.LINE_START;
			gbc.fill = GridBagConstraints.BOTH;
			gbc.gridx = 0;
			gbc.gridy = y;
			this.domainPanel.add(new JLabel(o.getName()), gbc);
			
			MyDeleteBT button = new MyDeleteBT();
			button.addActionListener(new RemoveFromPanelActionListener(this));
			button.setActionCommand("Domain");
			gbc.gridx = 1;
			gbc.gridy = y;
			this.domainPanel.add(button, gbc);
			
			domainMapper.put(button, o); // map domain to button
			y++;
		}
		this.domainPanel.repaint();
		this.domainPanel.revalidate();
	}
	
	protected void fillRangePanel(OWLProperty property) {
		this.rangeMapper = new HashMap<MyDeleteBT, String>(); //clear hashmap
		this.rangePanel.removeAll(); //clear panel
		List<String> rangeList = null;
		if(property instanceof MyOWLObjectProperty) {
			MyOWLObjectProperty prop = (MyOWLObjectProperty)property;
			rangeList = new ArrayList<String>(prop.getRanges());
		} else if(property instanceof MyOWLDataProperty) {
			MyOWLDataProperty prop = (MyOWLDataProperty)property;
			rangeList = new ArrayList<String>(prop.getRanges());
		}
		Iterator<String> iter = rangeList.iterator();
		int y = 0;
		while(iter.hasNext()) {
			String s = iter.next();
			GridBagConstraints gbc = new GridBagConstraints();
			gbc.insets = new Insets(0, 0, 5, 20);
			gbc.anchor = GridBagConstraints.LINE_START;
			gbc.fill = GridBagConstraints.BOTH;
			gbc.gridx = 0;
			gbc.gridy = y;
			this.rangePanel.add(new JLabel(s), gbc);
			
			MyDeleteBT button = new MyDeleteBT();
			button.addActionListener(new RemoveFromPanelActionListener(this));
			button.setActionCommand("Range");
			gbc.gridx = 1;
			gbc.gridy = y;
			this.rangePanel.add(button, gbc);
			
			rangeMapper.put(button, s); // map range to button
			y++;
		}
		this.rangePanel.repaint();
		this.rangePanel.revalidate();
	}
	
	//TODO serves for debug
	protected void writeTree(List<? extends OWLComponent> classes) {
		for(OWLComponent o : classes) {
			System.out.println(o.getName());
			if(o.hasSubComponent()) {
				this.writeTree(o.getSubComponents());
			}
		}
	}
	
	private class RemoveFromPanelActionListener implements ActionListener {
		private OWLPropertiesPanel p;
		public RemoveFromPanelActionListener(OWLPropertiesPanel p) {
			this.p = p;
		}
		
		@Override
		public void actionPerformed(ActionEvent e) {
			TreePath currentSelection = p.tree.getSelectionPath();
			String selectedPropertyName = null;
			if (currentSelection != null) {
				DefaultMutableTreeNode currentNode = (DefaultMutableTreeNode) (currentSelection.getLastPathComponent());
				MutableTreeNode parent = (MutableTreeNode) (currentNode.getParent());
				if (parent != null) {
					selectedPropertyName = currentNode.getUserObject().toString();
				}
			}
			if(selectedPropertyName != null && !"".equals(selectedPropertyName)) {
				MyOWLObjectProperty obj = (MyOWLObjectProperty)p.findOWLComponentByName(selectedPropertyName, p.parent.getObjectPropertyList());
				MyOWLDataProperty data = (MyOWLDataProperty)p.findOWLComponentByName(selectedPropertyName, p.parent.getDataPropertyList());
				
				if("Domain".equals(e.getActionCommand())) {
					if(obj != null) {
						obj.getDomains().remove(p.domainMapper.get((MyDeleteBT)e.getSource()));
						p.fillDomainPanel(obj);
						logger.debug("Object Property domain removed!");
					}
					if(data != null) {
						data.getDomains().remove(p.domainMapper.get((MyDeleteBT)e.getSource()));
						p.fillDomainPanel(data);
						logger.debug("Data Property domain removed!");
					}
				} 
				if("Range".equals(e.getActionCommand())) {
					if (obj != null) {
						obj.getRanges().remove(p.rangeMapper.get((MyDeleteBT)e.getSource()));
						p.fillRangePanel(obj);
						logger.debug("Object Property range removed!");
					}
					if(data != null) {
						data.getRanges().remove(p.rangeMapper.get((MyDeleteBT)e.getSource()));
						p.fillRangePanel(data);
						logger.debug("Data Property range removed!");
					}
				}
			}
		}
		
	}
}
